package com.woniu.controller;

import com.alibaba.fastjson.JSONObject;
import com.woniu.entity.Result;
import com.woniu.entity.dto.UserLoginDTO;
import com.woniu.pojo.User;
import com.woniu.service.IUserService;
import com.woniu.utils.Base64;
import com.woniu.utils.GiteeHttpClient;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.net.URLEncoder;

/**
 * 用户控制器
 * @author tarnett
 */
@RestController
@RequestMapping("/user")
@Slf4j
@Api(tags = "用户管理")
public class UserController {

    @Autowired
    private AuthenticationManager authenticationManager;
    @Value("${gitee.clientID}")
    private String clientID;
    @Value("${gitee.clientSecret}")
    private String clientSecret;
    @Value("${gitee.giteeReturnUrl}")
    private String giteeReturnUrl;
    @Autowired
    private IUserService userService;
    @Value("${woniu.baseUrl}")
    private String baseUrl;

    @ApiOperation("登录")
    @PostMapping("/login")
    public Result login(@Valid @RequestBody UserLoginDTO userLoginDTO){
        // 1. 数据进行规则校验
        /*if(StringUtils.isEmpty(userLoginDTO.getAccount())){
            return Result.error("请输入用户名");
        }
        if(StringUtils.isEmpty(userLoginDTO.getPassword())){
            return Result.error("请输入密码");
        }*/

        // 判断是登录操作还是绑定操作。如果openId为空，则为登录操作，否则为绑定操作
        if(!StringUtils.isEmpty(userLoginDTO.getOpenId())){
            // 根据用户名查找用户，如果用户不存在，则创建用户，并且将该用户与openId绑定
            User user = userService.getByAccount(userLoginDTO.getAccount());

            // 对openId进行解码操作
            String decodeOpenId = new String(Base64.decode(userLoginDTO.getOpenId()));
            userLoginDTO.setOpenId(decodeOpenId);

            if(user == null){
                // 进行注册操作
                Result result = userService.register(userLoginDTO);
                if(result.getCode() == 50000){
                    return result;
                }
            }else{
                // 判断密码是否正确
                BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
                boolean matches = bCryptPasswordEncoder.matches(userLoginDTO.getPassword(), user.getPassword());
                if(!matches){
                    return Result.error("用户名或者密码错误");
                }
            }
        }

        // 2. 封装一个用户名密码对象
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(userLoginDTO.getAccount(), userLoginDTO.getPassword());
        // 3. 调用认证管理器的认证方法(如果登录失败该方法会直接抛异常)
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);
        // 4. 将登录的用户信息保存到security的上下文中
        SecurityContextHolder.getContext().setAuthentication(authenticate);

        // 如果openId不为空，还需要将openId保存到对应的用户表里面
        userService.updateOpenIdByAccount(userLoginDTO.getOpenId(), userLoginDTO.getAccount());

        return Result.ok();
    }

    @ApiOperation("获取登录用户信息")
    @GetMapping("/info")
    public Result<User> getUserInfo(){
        UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        User user = new User();
        user.setAccount(userDetails.getUsername());
        return Result.ok(user);
    }

    @ApiOperation("注销")
    @PostMapping("/logout")
    public Result logout() {
        // 清除security的上下文
        SecurityContextHolder.clearContext();
        return Result.ok();
    }

    @ApiOperation("gitee授权登录")
    @GetMapping("/toGiteeAuth")
    public void toGiteeAuth(@ApiIgnore HttpServletResponse response) throws Exception {
        // 1. 拼接gitee授权的地址
        String url = "https://gitee.com/oauth/authorize?response_type=code" +
                "&client_id=" + clientID  +
                "&redirect_uri=" + URLEncoder.encode(giteeReturnUrl, "UTF-8") +
                "&scope=user_info";
        // 2. 重定向操作
        log.debug("请求的地址为：{}", url);
        response.sendRedirect(url);
    }

    @ApiOperation("gitee授权登录回调地址")
    @GetMapping("/auth")
    public void auth(@ApiIgnore HttpServletResponse response, @ApiIgnore HttpServletRequest request) throws Exception {
        // 1. 获取用户授权之后的code,类似于验证码，通过该验证码去获取通行证AccessToken
        String code = request.getParameter("code");
        log.debug("gitee返回的code为：{}", code);

        // 2. 通过code去请求gitee获取通行证（授权码）AccessToken
        String url = "https://gitee.com/oauth/token?grant_type=authorization_code" +
                "&client_id=" + clientID +
                "&client_secret=" + clientSecret +
                "&code=" + code +
                "&redirect_uri=" + giteeReturnUrl;
        JSONObject jsonObject = GiteeHttpClient.getAccessToken(url);
        String accessToken = jsonObject.getString("access_token");
        log.debug("gitee返回的accessToken为：{}", accessToken);

        // 3. 通过AccessToken去获取gitee账户的个人信息
        url = "https://gitee.com/api/v5/user?access_token=" + accessToken;
        JSONObject userInfo = GiteeHttpClient.getUserInfo(url);
        log.debug("根据accessToken获取gitee账号的信息为：{}", userInfo);

        // 4. 可以通过openId去数据库查找是否有绑定的用户
        String openId = userInfo.getString("id");
        log.debug("根据userInfo获取的ID：{}", openId);
        User user = userService.getByOpenId(openId);

        // 4.1 存在绑定的用户，将该用户置为登录状态
        if(user != null){
            // 执行登录操作
            UsernamePasswordAuthenticationToken authenticationToken =
                    new UsernamePasswordAuthenticationToken(user.getAccount(), "");
            // 3. 调用认证管理器的认证方法(如果登录失败该方法会直接抛异常)
            Authentication authenticate = authenticationManager.authenticate(authenticationToken);
            // 4. 将登录的用户信息保存到security的上下文中
            SecurityContextHolder.getContext().setAuthentication(authenticate);
            response.sendRedirect(baseUrl + "pages/main.html");
        }else{
            // 5. 不存在绑定的用户，跳转到绑定用户的页面
            // 为了防止敏感数据暴露的问题，将openId进行编码处理
            String baseOpenId = Base64.encode(openId.getBytes());
            response.sendRedirect(baseUrl + "binding.html?openId=" + baseOpenId);
        }
    }
}
